package com.xbx.zuul.auth.config;

import com.xbx.common.utils.MyUtils;
import com.xbx.zuul.auth.MyShiroCasRealm;
import com.xbx.zuul.cache.FilterCache;
import com.xbx.zuul.dataobject.UserDOAndRoleDO;
import com.xbx.zuul.filter.AnyOfRolesAuthorizationFilter;
import com.xbx.zuul.filter.MyFormAuthenticationFilter;
import com.xbx.zuul.mapper.UserRoleDOMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.RedisManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.Filter;
import java.util.*;

/**
 * shiro+cas 配置
 */
@Configuration
@Slf4j
public class ShiroConfiguration {

    // cas server地址
    @Value("${casServerUrlPrefix}")
    public String casServerUrlPrefix;
    // Cas登录页面地址
    @Value("${casLoginUrl}")
    public String casLoginUrl;
    // Cas登出页面地址
    @Value("${casLogoutUrl}")
    public String casLogoutUrl;
    // 当前工程对外提供的服务地址
    @Value("${shiroServerUrlPrefix}")
    public String shiroServerUrlPrefix;
    // casFilter UrlPattern
    public static final String CAS_FILTER_URL_PATTERN = "/cas";
    // 登录地址
    @Value("${loginUrl}")
    public String loginUrl;
    // 登出地址
    @Value("${logoutUrl}")
    public String logoutUrl;
    // 登录成功地址
    @Value("${loginSuccessUrl}")
    public String loginSuccessUrl;
    // 权限认证失败跳转地址
    @Value("${unauthorizedUrl}")
    public String unauthorizedUrl;

    // 内置角色
    public static final String SYSTEM_ROLE = "system";

    @Value("${safetyUrl}")
    private String safetyUrl="";

    @Autowired
    private FilterCache filterCache;

    /*@Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.sentinel.nodes}")
    private String redisNodes;

    @Value("${spring.redis.sentinel.master}")
    private String master;*/

    @Value("${notpermissionHost}")
    private String notpermissionHost;

    /*@Bean
    public MyRedisCacheManager getRedisCacheManager() {

        MyRedisCacheManager em = new MyRedisCacheManager();

        em.setRedisManager(redisSentinelManager());
        return em;
    }


    @Bean
    public RedisSentinelManager redisSentinelManager() {
        RedisSentinelManager redisSentinelManager = new RedisSentinelManager();
        redisSentinelManager.setMasterName(master);
        redisSentinelManager.setHost(redisNodes);
        redisSentinelManager.setPassword(password);
        return redisSentinelManager;
    }*/

    @Bean
    public MyRedisCacheManager getRedisCacheManager() {

        MyRedisCacheManager em = new MyRedisCacheManager();

        em.setRedisManager(getRedisManager());
        return em;
    }

    @Bean
    public RedisManager getRedisManager() {

        RedisManager em = new RedisManager();

        return em;
    }

   /* @Bean
    public EhCacheManager getEhCacheManager() {
        EhCacheManager em = new EhCacheManager();
        em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        return em;
    }*/

    @Bean(name = "myShiroCasRealm")
    public MyShiroCasRealm myShiroCasRealm() {
        MyShiroCasRealm realm = new MyShiroCasRealm();
        realm.setCacheManager(getRedisCacheManager());
        realm.setAuthenticationCachingEnabled(true);
        return realm;
    }

    /**
     * 注册DelegatingFilterProxy（Shiro）
     */
    @Bean
    public FilterRegistrationBean delegatingFilterProxy() {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        //  该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(MyShiroCasRealm myShiroCasRealm) {
        DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
        dwsm.setRealm(myShiroCasRealm);
//      <!-- 用户授权/认证信息Cache, 采用redisCache 缓存 -->
        dwsm.setCacheManager(getRedisCacheManager());
        return dwsm;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
        aasa.setSecurityManager(securityManager);
        return aasa;
    }

    /*@Bean
    public LogoutFilter logoutFilter() {

        LogoutFilter logoutFilter = new LogoutFilter();

        logoutFilter.setRedirectUrl(logoutUrl);

        return logoutFilter;
    }*/

    /**
     * 自定义角色过滤器
     */
    @Bean(name = "anyofroles")
    public AnyOfRolesAuthorizationFilter getAnyOfRolesAuthorizationFilter() {

        AnyOfRolesAuthorizationFilter anyOfRolesAuthorizationFilter = new AnyOfRolesAuthorizationFilter();

        anyOfRolesAuthorizationFilter.setNotpermissionHost(notpermissionHost);

        return anyOfRolesAuthorizationFilter;
    }

    /**
     * 自定义角色过滤器
     */
    @Bean
    public MyFormAuthenticationFilter getMyFormAuthenticationFilter() {

        return new MyFormAuthenticationFilter();
    }

    /**
     * 自定义角色过滤器
     */
    @Bean
    public FilterRegistrationBean registration(MyFormAuthenticationFilter myFormAuthenticationFilter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(myFormAuthenticationFilter);
        registration.setEnabled(false);
        return registration;
    }

    /**
     * ShiroFilter<br/>
     * 注意这里参数中的 StudentService 和 IScoreDao 只是一个例子，因为我们在这里可以用这样的方式获取到相关访问数据库的对象，
     * 然后读取数据库相关配置，配置到 shiroFilterFactoryBean 的访问规则中。实际项目中，请使用自己的Service来处理业务逻辑。
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager, UserRoleDOMapper userRoleDOMapper) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/LoginController.jsp"页面
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        // 登录成功后要跳转的连接
        shiroFilterFactoryBean.setSuccessUrl(loginSuccessUrl);
        shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
        // 添加casFilter到shiroFilter中
        Map<String, Filter> filters = new HashMap<>();
        filters.put("authc", new MyFormAuthenticationFilter());
        shiroFilterFactoryBean.setFilters(filters);

        loadShiroFilterChain(shiroFilterFactoryBean, userRoleDOMapper);
        return shiroFilterFactoryBean;
    }

    /**
     * 加载shiroFilter权限控制规则（从数据库读取然后配置）,角色/权限信息由MyShiroCasRealm对象提供doGetAuthorizationInfo实现获取来的
     */
    private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean, UserRoleDOMapper userRoleDOMapper){
        /////////////////////// 下面这些规则配置最好配置到配置文件中 ///////////////////////
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        // authc：该过滤器下的页面必须登录后才能访问，它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
        // anon: 可以理解为不拦截
        // user: 登录了就不拦截
        // roles["admin"] 用户拥有admin角色
        // perms["permission1"] 用户拥有permission1权限
        // filter顺序按照定义顺序匹配，匹配到就验证，验证完毕结束。
        // url匹配通配符支持：? * **,分别表示匹配1个，匹配0-n个（不含子路径），匹配下级所有路径

        //logut请求采用logout filter
        //filterChainDefinitionMap.put("/logout","logout");

        //2.不拦截的请求
        if(StringUtils.isNotEmpty(this.safetyUrl)){

            // 安全的请求url
            List<String> urls = Arrays.asList(this.safetyUrl.split(","));
            for(String url : urls){

                filterChainDefinitionMap.put(url, DefaultFilter.anon.name());
            }
        }
        // 默认配置
        filterChainDefinitionMap.put("/**/swagger-ui.html","anon");
        filterChainDefinitionMap.put("/**/swagger/**","anon");
        filterChainDefinitionMap.put("/**/webjars/**", "anon");
        filterChainDefinitionMap.put("/**/swagger-resources/**","anon");
        filterChainDefinitionMap.put("/**/v2/**","anon");

        //filterChainDefinitionMap.put("/userController/**", "anon");


        //3.拦截的请求（从本地数据库获取或者从casserver获取(webservice,http等远程方式)，看你的角色权限配置在哪里）
        List<UserDOAndRoleDO> userDOAndRoleDOS = userRoleDOMapper.selectAllRoleInfos();

        // 加载角色权限
        if(MyUtils.listIsNotNull(userDOAndRoleDOS)){

            this.loadUserRolePermission(userDOAndRoleDOS, filterChainDefinitionMap);
        }

        //  所有请求都需要拦截
        filterChainDefinitionMap.put("/**", "authc");
        //filterChainDefinitionMap.put("/user/add/**", "authc,roles[admin]"); //需要登录，且用户角色为admin
        //filterChainDefinitionMap.put("/user/delete/**", "authc,perms[\"user:delete\"]"); //需要登录，且用户有权限为user:delete

        //4.登录过的不拦截
        // filterChainDefinitionMap.put("/**", "user");

        log.info("启动系统初始化权限过滤器，当前过滤器详情为{}", filterChainDefinitionMap.toString());

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        // 主动更新，和缓存同步一个版本号
        filterCache.setExsistFilterVersion();
    }

    /**
     * 加载角色权限
     */
    private void loadUserRolePermission(List<UserDOAndRoleDO> userDOAndRoleDOS, Map<String, String> filterChainDefinitionMap) {

        for (UserDOAndRoleDO userDOAndRoleDO : userDOAndRoleDOS) {

            if (StringUtils.isNotEmpty(userDOAndRoleDO.getActionUrl())) {

                if (StringUtils.isEmpty(userDOAndRoleDO.getRoleCode())) {

                    // 使用内置角色保护url
                    filterChainDefinitionMap.put(userDOAndRoleDO.getActionUrl(), "anyofroles[\"" + SYSTEM_ROLE + "\"]");
                } else {

                    filterChainDefinitionMap.put(userDOAndRoleDO.getActionUrl(), "anyofroles[\"" + userDOAndRoleDO.getRoleCode() + "\"]");
                }
            }
        }
    }
}
